# pragma once

# include <pthread.h>

# include <vector>

namespace threading {

    template <typename Task>
    class TaskPool {
        typedef std::vector<pthread_t> threads_container;
        typedef std::vector<Task*> tasks_container;
    public:

        TaskPool(size_t nt) : numThreads_(nt), queue_(), counter_(0) {}

        void add(Task* task) {
            queue_.push_back(task);
        }

        void run() {
            threads_.reserve(numThreads_);
            for (size_t i = 0; i < numThreads_; ++i) {
                pthread_t threadId;
                pthread_create(&threadId, NULL, run, this);
                threads_.push_back(threadId);
            }
        }

        void wait() {
            typedef threads_container::const_iterator iterator;
            for (iterator iter = threads_.begin(); iter != threads_.end(); ++iter) {
                pthread_join(*iter, NULL);
            }
        }

    private:

        Task* getTask() {
            size_t taskId = __sync_fetch_and_add(&counter_, 1);
            if (taskId < queue_.size())
                return queue_[taskId];
            else
                return NULL;
        }

        static void* run(void* arg) {
            TaskPool& pool = *static_cast<TaskPool*>(arg);

            while (true) {
                Task* task = pool.getTask();
                if (task == NULL)
                    break;
                else 

                    (*task)();
            }

            return NULL;
        }

    private:

        /*! @brief Number of execution threads
         */
        size_t const numThreads_;

        /*! @brief Tasks queue
         */
        std::vector<Task*> queue_;

        /*! @brief Container of threads
         */
        std::vector<pthread_t> threads_;

        /*! @brief Task counter
         */
        volatile size_t counter_;
    };

}
